#include "CEGUIGamebryoRenderer.h"
#include "CEGUIGamebryoTexture.h"
#include "CEGUIExceptions.h"
#include "CEGUISystem.h"

//---------------------------------------------------------------------------
NiMeshScreenElements* CreateElement(CEGUIGamebryoTexture *pkBaseTexture)
{
    NiMeshScreenElements *pkElement = NiNew NiMeshScreenElements(false, true, 1,
		32, 32, 128, 128, 64, 64);

    // Use alpha blending
    NiAlphaProperty* pkAlphaProp = NiNew NiAlphaProperty();
    pkAlphaProp->SetAlphaBlending(true);
    pkElement->AttachProperty(pkAlphaProp);

    // Don't use lighting.
    NiVertexColorProperty* pkVertexColorProp = 
        NiNew NiVertexColorProperty();
    pkVertexColorProp->SetSourceMode(
        NiVertexColorProperty::SOURCE_EMISSIVE);
    pkVertexColorProp->SetLightingMode(NiVertexColorProperty::LIGHTING_E);
    pkElement->AttachProperty(pkVertexColorProp);

    // Set texture
    NiTexturingProperty *pkTexturingProp = NiNew NiTexturingProperty;
    if(pkBaseTexture)
        pkTexturingProp->SetBaseTexture(pkBaseTexture->m_spGamebryoTexture);
    pkElement->AttachProperty(pkTexturingProp);

    pkElement->Insert(4);
    pkElement->UpdateProperties();

    return pkElement;
}
//---------------------------------------------------------------------------
CEGUIGamebryoRenderer::CEGUIGamebryoRenderer(NiRenderer *pkGamebryoRenderer)
{
	m_pkGamebryoRenderer = pkGamebryoRenderer;
	m_spImmediateElement = 0;

	clearRenderList();
    d_identifierString = "CEGUIGamebryoRenderer - Unofficial Gamebryo based "
		"renderer module for CEGUI";

    m_spImmediateElement = CreateElement(NULL);
}
//---------------------------------------------------------------------------
CEGUIGamebryoRenderer::~CEGUIGamebryoRenderer(void)
{
	destroyAllTextures();
}
//---------------------------------------------------------------------------
void CEGUIGamebryoRenderer::addQuad(const Rect& dest_rect, float z, 
	const Texture* tex, const Rect& texture_rect, const ColourRect& colours, 
	QuadSplitMode quad_split_mode)
{
	int iHandle = 0;
	NiMeshScreenElements *pkElement;

	if(!m_bQueuing)
	{
        pkElement = m_spImmediateElement;

        // Set texture
        CEGUIGamebryoTexture *pkTex = (CEGUIGamebryoTexture*)tex;
        NiTexturingProperty *pkTexturingProp = (NiTexturingProperty *)
            pkElement->GetProperty(NiProperty::TEXTURING);
        pkTexturingProp->SetBaseTexture(pkTex->m_spGamebryoTexture);
	}
	// We are using queuing and are trying to add a second quad to the same
	// NiMeshScreenElements, so just insert a second rect
	else if(m_pkPreviousTexture == tex)
	{
		pkElement = m_kMeshScreenElementsSet.GetAt(m_uiActiveElements - 1);
		iHandle = pkElement->Insert(4);
	}
    // A texture change has happened, switch to next NiMeshScreenElements
	else
	{
        // We need more NiMeshScreenElements to accommodate this quad.
        if(m_uiActiveElements >= m_kMeshScreenElementsSet.GetSize())
        {
            pkElement = CreateElement((CEGUIGamebryoTexture*)tex);
            m_kMeshScreenElementsSet.Add(pkElement);
        }
        // We have an NiMeshScreenElements that hasn't been used for this frame.
        else
        {
            pkElement = m_kMeshScreenElementsSet.GetAt(m_uiActiveElements);
            iHandle = pkElement->Insert(4);

            // Set texture
            CEGUIGamebryoTexture *pkTex = (CEGUIGamebryoTexture*)tex;
            NiTexturingProperty *pkTexturingProp = (NiTexturingProperty *)
                pkElement->GetProperty(NiProperty::TEXTURING);
            pkTexturingProp->SetBaseTexture(pkTex->m_spGamebryoTexture);
        }

		m_uiActiveElements++;
		m_pkPreviousTexture = (Texture*)tex;
	}

	// Set all the incoming values of the quad
	float fWidth = 
		(float)m_pkGamebryoRenderer->GetDefaultBackBuffer()->GetWidth();
	float fHeight = 
		(float)m_pkGamebryoRenderer->GetDefaultBackBuffer()->GetHeight();
	pkElement->SetRectangle(iHandle, 
		dest_rect.d_left / fWidth, dest_rect.d_top / fHeight,
		dest_rect.getWidth() / fWidth, dest_rect.getHeight() / fHeight);
	pkElement->SetTextures(iHandle, 0, 
		texture_rect.d_left, texture_rect.d_top,
		texture_rect.d_right, texture_rect.d_bottom);
	pkElement->SetColors(iHandle, 
		NiColorA(colours.d_top_left.getRed(), 
			colours.d_top_left.getGreen(), 
			colours.d_top_left.getBlue(), 
			colours.d_top_left.getAlpha()), 
		NiColorA(colours.d_bottom_left.getRed(), 
			colours.d_bottom_left.getGreen(), 
			colours.d_bottom_left.getBlue(), 
			colours.d_bottom_left.getAlpha()), 
		NiColorA(colours.d_bottom_right.getRed(), 
			colours.d_bottom_right.getGreen(), 
			colours.d_bottom_right.getBlue(), 
			colours.d_bottom_right.getAlpha()), 
		NiColorA(colours.d_top_right.getRed(), 
			colours.d_top_right.getGreen(), 
			colours.d_top_right.getBlue(), 
			colours.d_top_right.getAlpha()));

    // When CEGUI turns of queuing, we must render immediately. The next
    // doRender call doesn't occur until the next frame.
    if(!m_bQueuing)
        pkElement->RenderImmediate(m_pkGamebryoRenderer);
}
//---------------------------------------------------------------------------
void CEGUIGamebryoRenderer::doRender(void)
{
	m_pkGamebryoRenderer->SetScreenSpaceCameraData();

	for(unsigned int i = 0; i < m_uiActiveElements; i++)
	{
		NiMeshScreenElements* pkElement = m_kMeshScreenElementsSet.GetAt(i);
        if (pkElement) pkElement->RenderImmediate(m_pkGamebryoRenderer);
	}
}
//---------------------------------------------------------------------------
void CEGUIGamebryoRenderer::clearRenderList(void)
{
	m_uiActiveElements = 0;
	m_pkPreviousTexture = 0;

	for(unsigned int i = 0; i < m_kMeshScreenElementsSet.GetSize(); i++)
	{
		NiMeshScreenElements* pkElement = m_kMeshScreenElementsSet.GetAt(i);
        if (pkElement)
			pkElement->RemoveAll();
	}
}
//---------------------------------------------------------------------------
Texture* CEGUIGamebryoRenderer::createTexture(void)
{
	CEGUIGamebryoTexture* tex = new CEGUIGamebryoTexture(this);
	m_kTextureList.AddTail(tex);
	return tex;
}
//---------------------------------------------------------------------------
Texture* CEGUIGamebryoRenderer::createTexture(const String& filename, 
											  const String& resourceGroup)
{
	CEGUIGamebryoTexture* tex = (CEGUIGamebryoTexture*)createTexture();
	try
	{
	    tex->loadFromFile(filename, resourceGroup);
	}
	catch (RendererException&)
	{
	    delete tex;
	    throw;
	}
	return tex;
}
//---------------------------------------------------------------------------
Texture* CEGUIGamebryoRenderer::createTexture(float size)
{
	return createTexture();
}
//---------------------------------------------------------------------------
void CEGUIGamebryoRenderer::destroyTexture(Texture* texture)
{
	if(texture)
		((CEGUIGamebryoTexture *)texture)->m_spGamebryoTexture = 0;
}
//---------------------------------------------------------------------------
void CEGUIGamebryoRenderer::destroyAllTextures(void)
{
	m_kMeshScreenElementsSet.RemoveAll();

	NiTListIterator iter = m_kTextureList.GetHeadPos();
	while(iter)
	{
		CEGUIGamebryoTexture *pkTex = m_kTextureList.GetNext(iter);
		destroyTexture(pkTex);
	}
	m_kTextureList.RemoveAll();
}
//---------------------------------------------------------------------------
